//-----------------------------------------------------------------------
// <copyright file="ScheduledTaskManagement.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Shaun Wilde (ICS)</author>
// <email>v-swilde</email>
// <date>2006-04-04</date>
// <summary>TODO</summary>
//-----------------------------------------------------------------------
namespace Microsoft.Sdc.Tasks.Configuration.ScheduledTask
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;

    #region Task related enumerations
    /// <summary>
    /// The TASK_TRIGGER_TYPE field of the TASK_TRIGGER structure determines
    /// which member of the TRIGGER_TYPE_UNION field to use.
    /// </summary>
    internal enum TASK_TRIGGER_TYPE : uint
    {
        /// <summary>
        /// Ignore the Type field.
        /// </summary>
        TASK_TIME_TRIGGER_ONCE = 0,  
        
        /// <summary>
        /// Use DAILY
        /// </summary>
        TASK_TIME_TRIGGER_DAILY = 1,  
        
        /// <summary>
        /// Use WEEKLY
        /// </summary>
        TASK_TIME_TRIGGER_WEEKLY = 2,  
        
        /// <summary>
        /// Use MONTHLYDATE
        /// </summary>
        TASK_TIME_TRIGGER_MONTHLYDATE = 3,  
        
        /// <summary>
        /// Use MONTHLYDOW
        /// </summary>
        TASK_TIME_TRIGGER_MONTHLYDOW = 4,  
        
        /// <summary>
        /// Ignore the Type field.
        /// </summary>
        TASK_EVENT_TRIGGER_ON_IDLE = 5,  
        
        /// <summary>
        /// Ignore the Type field.
        /// </summary>
        TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 6,  
        
        /// <summary>
        /// Ignore the Type field.
        /// </summary>
        TASK_EVENT_TRIGGER_AT_LOGON = 7   
    }

    /// <summary>
    /// Enumerate the days of the week
    /// </summary>
    [Flags]
    internal enum DAYS_OF_WEEK : ushort 
    {
        /// <summary>
        /// Sunday
        /// </summary>
        TASK_SUNDAY = 0x1,

        /// <summary>
        /// Monday
        /// </summary>
        TASK_MONDAY = 0x2,

        /// <summary>
        /// Tuesday
        /// </summary>
        TASK_TUESDAY = 0x4,

        /// <summary>
        /// Wednesday
        /// </summary>
        TASK_WEDNESDAY = 0x8,

        /// <summary>
        /// Thursday
        /// </summary>
        TASK_THURSDAY = 0x10,

        /// <summary>
        /// Friday
        /// </summary>
        TASK_FRIDAY = 0x20,

        /// <summary>
        /// Saturday
        /// </summary>
        TASK_SATURDAY = 0x40
    }

    /// <summary>
    /// Enumerate the weeks of the month
    /// </summary>
    internal enum WEEKS_OF_MONTH : ushort
    {
        /// <summary>
        /// First week
        /// </summary>
        TASK_FIRST_WEEK = 1,

        /// <summary>
        /// Second week
        /// </summary>
        TASK_SECOND_WEEK = 2,

        /// <summary>
        /// Third week
        /// </summary>
        TASK_THIRD_WEEK = 3,

        /// <summary>
        /// Fourth week
        /// </summary>
        TASK_FOURTH_WEEK = 4,

        /// <summary>
        /// Last week
        /// </summary>
        TASK_LAST_WEEK = 5,
    }

    /// <summary>
    /// Enumerate the months of the year
    /// </summary>
    [Flags]
    internal enum MONTHS_OF_YEAR : ushort
    {
        /// <summary>
        /// January
        /// </summary>
        TASK_JANUARY = 0x1,

        /// <summary>
        /// February
        /// </summary>
        TASK_FEBRUARY = 0x2,

        /// <summary>
        /// March
        /// </summary>
        TASK_MARCH = 0x4,

        /// <summary>
        /// April
        /// </summary>
        TASK_APRIL = 0x8,

        /// <summary>
        /// May
        /// </summary>
        TASK_MAY = 0x10,

        /// <summary>
        /// June
        /// </summary>
        TASK_JUNE = 0x20,

        /// <summary>
        /// July
        /// </summary>
        TASK_JULY = 0x40,

        /// <summary>
        /// August
        /// </summary>
        TASK_AUGUST = 0x80,

        /// <summary>
        /// September
        /// </summary>
        TASK_SEPTEMBER = 0x100,

        /// <summary>
        /// October
        /// </summary>
        TASK_OCTOBER = 0x200,

        /// <summary>
        /// November
        /// </summary>
        TASK_NOVEMBER = 0x400,

        /// <summary>
        /// December
        /// </summary>
        TASK_DECEMBER = 0x800
    }

    /// <summary>
    /// Used by IScheduledWorkItem::SetFlags
    /// 
    /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
    /// </summary>
    [Flags]
    internal enum TASK_FLAG : uint
    {
        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_INTERACTIVE = 0x1,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_DELETE_WHEN_DONE = 0x2,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_DISABLED = 0x4,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_START_ONLY_IF_IDLE = 0x10,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_KILL_ON_IDLE_END = 0x20,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_HIDDEN = 0x200,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_SYSTEM_REQUIRED = 0x1000,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/ischeduledworkitem_setflags.asp
        /// </summary>
        TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
    }

    /// <summary>
    /// used by the Flags field of <see cref="TASK_TRIGGER"/>
    /// 
    /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/task_trigger.asp
    /// </summary>
    [Flags]
    internal enum TASK_TRIGGER_FLAG : uint
    {
        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/task_trigger.asp
        /// </summary>
        TASK_TRIGGER_FLAG_HAS_END_DATE = 0x1,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/task_trigger.asp
        /// </summary>
        TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0x2,

        /// <summary>
        /// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/taskschd/taskschd/task_trigger.asp
        /// </summary>
        TASK_TRIGGER_FLAG_DISABLED = 0x4,
    }
    #endregion

    #region Task related structures
    /// <summary>
    /// The data that is required when creating a Daily task
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct DAILY
    {
        /// <summary>
        /// Interval (in days) when task is scheduled
        /// </summary>
        public ushort DaysInterval;
    }

    /// <summary>
    /// The data that is required when creating a Weekly task
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct WEEKLY
    {
        /// <summary>
        /// The interval (in weeks) when task is scheduled
        /// </summary>
        public ushort WeeksInterval;

        /// <summary>
        /// Which days is the task scheduled to run
        /// </summary>
        public DAYS_OF_WEEK DaysOfTheWeek;
    }

    /// <summary>
    /// Defines the day of the month the task will run
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct MONTHLYDATE
    {
        /// <summary>
        /// Specifies the day of the month a task runs. This value is a bit field that specifies the day(s) the task will run. Bit 0 corresponds to the first of the month, bit 1 to the second, and so forth. 
        /// </summary>
        public uint Days;

        /// <summary>
        /// Specifies the month(s) when the task runs.
        /// </summary>
        public MONTHS_OF_YEAR Months;
    }

    /// <summary>
    /// Defines the date(s) that the task runs by month, week, and day of the week.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct MONTHLYDOW
    {
        /// <summary>
        /// Specifies the week of the month when the task runs.
        /// </summary>
        public WEEKS_OF_MONTH WhichWeek;

        /// <summary>
        /// Specifies the day(s) of the week (specified in wWhichWeek) when the task runs. 
        /// </summary>
        public DAYS_OF_WEEK DaysOfTheWeek;

        /// <summary>
        /// Value that describes the month(s) when the task runs.
        /// </summary>
        public MONTHS_OF_YEAR Months;
    }

    /// <summary>
    /// Defines the invocation schedule of the trigger within the Type member of a TASK_TRIGGER structure.
    /// </summary>
    [StructLayout(LayoutKind.Explicit)]
    internal struct TRIGGER_TYPE_UNION
    {
        /// <summary>
        /// A DAILY structure that specifies the number of days between invocations of a task. 
        /// </summary>
        [FieldOffset(0)]
        public DAILY Daily;

        /// <summary>
        /// A WEEKLY structure that specifies the number of weeks between invocations of a task, and day(s) of the week the task will run.
        /// </summary>
        [FieldOffset(0)]
        public WEEKLY Weekly;

        /// <summary>
        /// A MONTHLYDATE structure that specifies the month(s) and day(s) of the month a task will run.
        /// </summary>
        [FieldOffset(0)]
        public MONTHLYDATE MonthlyDate;

        /// <summary>
        /// A MONTHLYDOW structure that specifies the day(s) of the year a task runs by month(s), week of month, and day(s) of week.
        /// </summary>
        [FieldOffset(0)]
        public MONTHLYDOW MonthlyDOW;
    }

    /// <summary>
    /// Defines the times to run a scheduled work item.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    internal struct TASK_TRIGGER
    {
        /// <summary>
        /// Structure size.
        /// </summary>
        public ushort TriggerSize;             
        
        /// <summary>
        /// Reserved. Must be zero.
        /// </summary>
        public ushort Reserved1;               

        /// <summary>
        /// Trigger beginning date year.
        /// </summary>
        public ushort BeginYear;               

        /// <summary>
        /// Trigger beginning date month.
        /// </summary>
        public ushort BeginMonth;              

        /// <summary>
        /// Trigger beginning date day.
        /// </summary>
        public ushort BeginDay;                

        /// <summary>
        /// Optional trigger ending date year.
        /// </summary>
        public ushort EndYear;                

        /// <summary>
        /// Optional trigger ending date month.
        /// </summary>
        public ushort EndMonth;                

        /// <summary>
        /// Optional trigger ending date day.
        /// </summary>
        public ushort EndDay;                  

        /// <summary>
        /// Run bracket start time hour.
        /// </summary>
        public ushort StartHour;               

        /// <summary>
        /// Run bracket start time minute.
        /// </summary>
        public ushort StartMinute;            

        /// <summary>
        /// Duration of run bracket.
        /// </summary>
        public uint MinutesDuration;           

        /// <summary>
        /// Run bracket repetition interval.
        /// </summary>
        public uint MinutesInterval;           

        /// <summary>
        /// Trigger flags.
        /// </summary>
        public TASK_TRIGGER_FLAG Flags;        

        /// <summary>
        /// Trigger type.
        /// </summary>
        public TASK_TRIGGER_TYPE Type;         

        /// <summary>
        /// Trigger data peculiar to this type (union).
        /// </summary>
        public TRIGGER_TYPE_UNION Data;       

        /// <summary>
        /// Reserved. Must be zero.
        /// </summary>
        public ushort Reserved2;               

        /// <summary>
        /// Maximum number of random minutes after start time.
        /// </summary>
        public ushort RandomMinutesInterval;   
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SystemTime
    {
        public ushort Year;
        public ushort Month;
        public ushort DayOfWeek;
        public ushort Day;
        public ushort Hour;
        public ushort Minute;
        public ushort Second;
        public ushort Milliseconds;
    }
    #endregion

    #region task related interfaces
    [Guid("148BD52B-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface ITaskTrigger
    {
        void SetTrigger([In, Out, MarshalAs(UnmanagedType.Struct)] ref TASK_TRIGGER Trigger);
        void GetTrigger([In, Out, MarshalAs(UnmanagedType.Struct)] ref TASK_TRIGGER Trigger);
        void GetTriggerString(out System.IntPtr TriggerString);
    }

    [Guid("a6b952f0-a4b1-11d0-997d-00aa006887ec"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IScheduledWorkItem
    {
        void CreateTrigger([Out] out ushort NewTriggerIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITaskTrigger Trigger);
        void DeleteTrigger([In] ushort TriggerIndex);
        void GetTriggerCount([Out] out ushort Count);
        void GetTrigger([In] ushort TriggerIndex, [Out, MarshalAs(UnmanagedType.Interface)] out ITaskTrigger Trigger);
        void GetTriggerString([In] ushort TriggerIndex, out System.IntPtr TriggerString);
        void GetRunTimes([In, MarshalAs(UnmanagedType.Struct)] ref SystemTime Begin, [In, MarshalAs(UnmanagedType.Struct)] ref SystemTime End, ref ushort Count, [Out] out System.IntPtr TaskTimes);
        void GetNextRunTime([In, Out, MarshalAs(UnmanagedType.Struct)] ref SystemTime NextRun);
        void SetIdleWait([In] ushort IdleMinutes, [In] ushort DeadlineMinutes);
        void GetIdleWait([Out] out ushort IdleMinutes, [Out] out ushort DeadlineMinutes);
        void Run();
        void Terminate();
        void EditWorkItem([In] uint hParent, [In] uint dwReserved);
        void GetMostRecentRunTime([In, Out, MarshalAs(UnmanagedType.Struct)] ref SystemTime LastRun);
        void GetStatus([Out, MarshalAs(UnmanagedType.Error)] out int Status);
        void GetExitCode([Out] out uint ExitCode);
        void SetComment([In, MarshalAs(UnmanagedType.LPWStr)] string Comment);
        void GetComment(out System.IntPtr Comment);
        void SetCreator([In, MarshalAs(UnmanagedType.LPWStr)] string Creator);
        void GetCreator(out System.IntPtr Creator);
        void SetWorkItemData([In] ushort DataLen, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U1)] byte[] Data);
        void GetWorkItemData([Out] out ushort DataLen, [Out] out System.IntPtr Data);
        void SetErrorRetryCount([In] ushort RetryCount);
        void GetErrorRetryCount([Out] out ushort RetryCount);
        void SetErrorRetryInterval([In] ushort RetryInterval);
        void GetErrorRetryInterval([Out] out ushort RetryInterval);
        void SetFlags([In] uint Flags);
        void GetFlags([Out] out uint Flags);
        void SetAccountInformation([In, MarshalAs(UnmanagedType.LPWStr)] string AccountName, [In, MarshalAs(UnmanagedType.LPWStr)] string Password);
        void GetAccountInformation(out System.IntPtr AccountName);
    }

    [Guid("148BD524-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface ITask : IScheduledWorkItem
    {
        void SetApplicationName([In, MarshalAs(UnmanagedType.LPWStr)] string ApplicationName);
        void GetApplicationName(out System.IntPtr ApplicationName);
        void SetParameters([In, MarshalAs(UnmanagedType.LPWStr)] string Parameters);
        void GetParameters(out System.IntPtr Parameters);
        void SetWorkingDirectory([In, MarshalAs(UnmanagedType.LPWStr)] string WorkingDirectory);
        void GetWorkingDirectory(out System.IntPtr WorkingDirectory);
        void SetPriority([In] uint Priority);
        void GetPriority([Out] out uint Priority);
        void SetTaskFlags([In] uint Flags);
        void GetTaskFlags([Out] out uint Flags);
        void SetMaxRunTime([In] uint MaxRunTimeMS);
        void GetMaxRunTime([Out] out uint MaxRunTimeMS);
    }

    [Guid("148BD528-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IEnumWorkItems
    {
        [PreserveSig()]
        int Next([In] uint RequestCount, [Out] out System.IntPtr Names, [Out] out uint Fetched);
        void Skip([In] uint Count);
        void Reset();
        void Clone([Out, MarshalAs(UnmanagedType.Interface)] out IEnumWorkItems EnumWorkItems);
    }

    [Guid("148BD527-A2AB-11CE-B11F-00AA00530503"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface ITaskScheduler
    {
        void SetTargetComputer([In, MarshalAs(UnmanagedType.LPWStr)] string Computer);
        void GetTargetComputer(out System.IntPtr Computer);
        void Enum([Out, MarshalAs(UnmanagedType.Interface)] out IEnumWorkItems EnumWorkItems);
        void Activate([In, MarshalAs(UnmanagedType.LPWStr)] string Name, [In] ref System.Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object obj);
        void Delete([In, MarshalAs(UnmanagedType.LPWStr)] string Name);
        void NewWorkItem([In, MarshalAs(UnmanagedType.LPWStr)] string TaskName, [In] ref System.Guid rclsid, [In] ref System.Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object obj);
        void AddWorkItem([In, MarshalAs(UnmanagedType.LPWStr)] string TaskName, [In, MarshalAs(UnmanagedType.Interface)] IScheduledWorkItem WorkItem);
        void IsOfType([In, MarshalAs(UnmanagedType.LPWStr)] string TaskName, [In] ref System.Guid riid);
    }
    #endregion

    /// <summary>
    /// Scheduled Task Management class deals with the interaction with ITaskScheduler
    /// 
    /// see
    /// %PROGRAMFILES%\Microsoft Platform SDK\Include\mstask.h
    /// %PROGRAMFILES%\Microsoft Platform SDK\Include\mstask.idl
    /// </summary>
    internal class ScheduledTaskManagement
    {
        /// <summary>
        /// convert a returned pointer (to a string) to a .NET string and free the data - this prevents memory leaks
        /// </summary>
        /// <param name="lpwstr"></param>
        /// <returns></returns>
        public static string LPWStrToString(System.IntPtr lpwstr)
        {
            string ret = Marshal.PtrToStringUni(lpwstr);
            Marshal.FreeCoTaskMem(lpwstr);
            return ret;
        }
    }
}
